package pl.enigmatic.parser.xml;

import java.lang.reflect.Array;
import java.util.ArrayList;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import pl.enigmatic.util.Reflection;
import pl.enigmatic.util.XML;
import pl.enigmatic.parser.string.ArrayConverter;
import pl.enigmatic.parser.string.Converter;


public class ArrayParser<T> extends Parser<T[]> {

	private final Class<?> base;
	private final int depth;
	private final Parser<T> parser;

	@SuppressWarnings("unchecked")
	protected ArrayParser(final Class<T[]> arraycls) {
		super(arraycls, false);
		base = Reflection.arrayBase(arraycls);
		depth = Reflection.arrayDepth(arraycls);
		if (depth == 1) {
			parser = (Parser<T>) createParser(base);
		} else {
			parser = (Parser<T>) createParser(Reflection.arrayType(base, depth - 1));
		}
	}

	@Override
	protected Converter<T[]> createConverter() {
		if (Reflection.arrayDepth(getClazz()) > ArrayConverter.MAX_DEPTH) {
			return null;
		} else {
			return super.createConverter();
		}
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public T[] parse(final ParsingInfo<T[]> info) {
		Element node = info.getElement();
		final NodeList children = node.getChildNodes();
		if (children.getLength() == 1 && node.getFirstChild().getNodeType() == Node.TEXT_NODE) {
			return convert(XML.getText(node), null);
		} else {
			final ArrayList<T> res = new ArrayList<T>();
			for (int i = 0; i < children.getLength(); i++) {
				if (children.item(i).getNodeType() == Node.ELEMENT_NODE) {
					final Element child = (Element) children.item(i);
					if (depth == 1) {
						String clazz = NodeNameInfo.getFullClassName(child, info.getStaticInfo());
						Parser parser = clazz == null ? this.parser : Parser.createParser(clazz);
						res.add((T) parser.parse(info.createChild(parser, null, child)));
					} else {
						res.add(parser.parse(info.createChild(parser, null, child)));
					}
				}
			}
			return res.toArray((T[]) Array.newInstance(parser.getClazz(), res.size()));
		}
	}

	/**
	 * @return the base
	 */
	public Class<?> getBase() {
		return base;
	}

	/**
	 * @return the depth
	 */
	public int getDepth() {
		return depth;
	}

}
